#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unordered_map>
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]#"
//命令行参数
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc=0;
//2.环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs=0;
//last exit code
int lastcode=0;
const char *GetUserName()
{
    const char *name=getenv("USER");
    return name ==NULL ? "None": name;
}

const char *GetHostName()
{
    const char* hostname=getenv("HOSTNAME");
    return hostname==NULL?"None":hostname;
    
}

const char* GetPwd()
{
    const char *pwd=getenv("PWD");
    return pwd==NULL?"None":pwd;
}

int Execute()
{
    pid_t id=fork();
    if(id==0)
    {
        //child
        execvp(g_argv[0],g_argv);
        exit(1);
    }

    //father
    pid_t rid=waitpid(id,nullptr,0);
    (void)rid;
    return 0;

}
std::string DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir=pwd;
    if(dir==SLASH) return SLASH;
    auto pos =dir.rfind(SLASH);
    if(pos==std::string::npos) return "BUG?";
    return dir.substr(pos+1);
}
void MakeCommandLine(char cmd_prompt[],int size)
{
    snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}

void PrintCommandPromt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt,sizeof(prompt));
    
    std::cout<<prompt;
    fflush(stdout);

}
//下面是shell定义的全局数据
//
bool GetCommanLine(char* out,int size)
{
    char* c=fgets(out,size,stdin);
    if(c==NULL) return false;
    out[strlen(out)-1]=0;
    if(strlen(out)==0) return false;
    return true;
}

bool CommandParse(char *commandline)
{
#define SEP " "
    g_argc=0;

    g_argv[g_argc++]=strtok(commandline,SEP);
    while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
    g_argc--;
    return g_argc>0 ? true:false;


}
const char* GetHome()
{
    const char* home=getenv("Home");
    return home==NULL ? "NONE" : home;
}
bool Cd()
{
    //cd atgc==1
    if(g_argc==1)
    {
        std::string home=GetHome();
        if(home.empty())
        {
            return true;

        }
        chdir(home.c_str());
    }
    else
    {
        std::string where =g_argv[1];
        //cd -/cd ~
        if(where=="-")
        {

        }
        else 
        {
            chdir(where.c_str());
        }
    }
    return true;
}
void Echo()
{
    if(g_argc==2)
    {
        //echo  "hellofucker"
        //echo $?
        //echo $PATH
        std::string opt=g_argv[1];
        if(opt=="$?")
        {
            std::cout<<lastcode<<std::endl;
            lastcode=0;
        }
        else if(opt[0]=='$')
        {
            std::string env_name=opt.substr(1);
            const char *env_value=getenv(env_name.c_str());
            if(env_value)
                std::cout<<env_value<<std::endl;
        }
        else
        {
            std::cout<<opt<<std::endl;
        }


    }
}
bool CheckAndExecBuiltin()
{
    std::string cmd=g_argv[0];
    if(cmd=="cd")
    {
        Cd();
        return true;
    }
    else if(cmd=="echo")
    {
        Echo();
        return true;
    }
    return false;
}
void InitEnv()
{
    extern char **environ;
    memset(g_env,0,sizeof(g_env));
    g_envs=0;

    //1.获取环境变量
    for(int i=0;environ[i];i++)
    {
        //申请空间
        g_env[i]=(char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i],environ[i]);
        g_envs++;
    }
    g_env[g_envs++]=(char*)"HAHAH=for_test";
    g_env[g_envs]=NULL;
    //2.导成环境变量
    for(int i=0;g_env[i];i++)
    {
        std::cout<<i<<' ';
        putenv(g_env[i]);
    }
    environ=g_env;
}
int main()
{
    InitEnv();
    while(true)
    {
        //输出命令行提示
        PrintCommandPromt();
        
        //2.获取用户输入的命令
        char commandline[COMMAND_SIZE];
        if(!GetCommanLine(commandline,sizeof(commandline)))
            continue;

        //3.命令行分析"ls -a -l"->"ls""-a""-l"
        if(!CommandParse(commandline))
            continue;
        //4.过滤内建命令
        if(CheckAndExecBuiltin())
            continue;
        //执行程序
        Execute();
    }
    return 0;
}

